using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore;
using ORS.OrderRobot.Domain;
using ORS.OrderRobot.IOC;

namespace ORS.OrderRobot.EFCore
{
    /// <summary>
    /// ef core仓储实现
    /// </summary>
    /// <typeparam name="TEntity"></typeparam>
    /// <typeparam name="TKey"></typeparam>
    public class EfCoreRepository<TEntity, TKey> : IRepository<TEntity, TKey>
        where TEntity : class, IEntity<TKey>
    {
        protected readonly OrsDbContext DbContext;
        protected readonly DbSet<TEntity> DbSet;

        public EfCoreRepository(OrsDbContext dbContext)
        {
            DbContext = dbContext;
            DbSet = dbContext.Set<TEntity>();
        }

        public virtual async Task<TEntity> GetAsync(TKey id, bool includeDetails = false, bool tracking = true, CancellationToken cancellationToken = default)
        {
            var query = DbSet.AsQueryable();
            
            if (!tracking)
            {
                query = query.AsNoTracking();
            }

            if (includeDetails)
            {
                query = IncludeDetails(query);
            }

            return await query.FirstOrDefaultAsync(e => e.Id.Equals(id), cancellationToken);
        }

        public virtual async Task<TEntity> GetAsync(Expression<Func<TEntity, bool>> predicate, bool includeDetails = false, bool tracking = true, CancellationToken cancellationToken = default)
        {
            var query = DbSet.AsQueryable();
            
            if (!tracking)
            {
                query = query.AsNoTracking();
            }

            if (includeDetails)
            {
                query = IncludeDetails(query);
            }

            return await query.FirstOrDefaultAsync(predicate, cancellationToken);
        }

        public virtual async Task<List<TEntity>> GetListAsync(Expression<Func<TEntity, bool>> predicate = null, bool includeDetails = false, bool tracking = false, CancellationToken cancellationToken = default)
        {
            var query = DbSet.AsQueryable();
            
            if (!tracking)
            {
                query = query.AsNoTracking();
            }

            if (includeDetails)
            {
                query = IncludeDetails(query);
            }

            if (predicate != null)
            {
                query = query.Where(predicate);
            }

            return await query.ToListAsync(cancellationToken);
        }

        public virtual async Task<List<TEntity>>  GetListBySortAsync(
            Expression<Func<TEntity, bool>> predicate = null,
            List<SortingRequest> sortingRequests = null,
            bool includeDetails = false,
            bool tracking = false,
            CancellationToken cancellationToken = default)
        {
            var query = DbSet.AsQueryable();

            if (!tracking)
            {
                query = query.AsNoTracking();
            }

            if (includeDetails)
            {
                query = IncludeDetails(query);
            }

            if (predicate != null)
            {
                query = query.Where(predicate);
            }

            // 应用排序
            if (sortingRequests != null && sortingRequests.Any())
            {
                query = ApplySorting(query, sortingRequests);
            }

            return await query.ToListAsync(cancellationToken);
        }

        public virtual async Task<long> GetCountAsync(Expression<Func<TEntity, bool>> predicate = null, CancellationToken cancellationToken = default)
        {
            var query = DbSet.AsQueryable();
            
            if (predicate != null)
            {
                query = query.Where(predicate);
            }

            return await query.LongCountAsync(cancellationToken);
        }

        public virtual async Task<PagedResult<TEntity>> GetPagedListAsync(
            Expression<Func<TEntity, bool>> predicate = null,
            PagedRequest pagedRequest = null,
            SortingRequest sortingRequest = null,
            bool includeDetails = false,
            bool tracking = false,
            CancellationToken cancellationToken = default)
        {
            return await GetPagedListAsync(
                predicate,
                pagedRequest,
                sortingRequest != null ? new List<SortingRequest> { sortingRequest } : null,
                includeDetails,
                tracking,
                cancellationToken);
        }

        public virtual async Task<PagedResult<TEntity>> GetPagedListAsync(
            Expression<Func<TEntity, bool>> predicate = null,
            PagedRequest pagedRequest = null,
            List<SortingRequest> sortingRequests = null,
            bool includeDetails = false,
            bool tracking = false,
            CancellationToken cancellationToken = default)
        {
            var query = DbSet.AsQueryable();
            
            if (!tracking)
            {
                query = query.AsNoTracking();
            }

            if (includeDetails)
            {
                query = IncludeDetails(query);
            }

            if (predicate != null)
            {
                query = query.Where(predicate);
            }

            // 应用排序
            if (sortingRequests != null && sortingRequests.Any())
            {
                query = ApplySorting(query, sortingRequests);
            }

            var totalCount = await query.LongCountAsync(cancellationToken);

            // 应用分页
            if (pagedRequest != null)
            {
                query = query.Skip((pagedRequest.PageIndex - 1) * pagedRequest.PageSize)
                            .Take(pagedRequest.PageSize);
            }

            var items = await query.ToListAsync(cancellationToken);

            return new PagedResult<TEntity>
            {
                TotalCount = totalCount,
                Items = items
            };
        }

        public virtual async Task<TEntity> InsertAsync(TEntity entity, CancellationToken cancellationToken = default)
        {
            await DbSet.AddAsync(entity, cancellationToken);
            await DbContext.SaveChangesAsync(cancellationToken);
            return entity;
        }

        public virtual async Task InsertManyAsync(IEnumerable<TEntity> entities, CancellationToken cancellationToken = default)
        {
            await DbSet.AddRangeAsync(entities, cancellationToken);
            await DbContext.SaveChangesAsync(cancellationToken);
        }

        public virtual async Task<TEntity> UpdateAsync(TEntity entity, CancellationToken cancellationToken = default)
        {
            DbContext.Entry(entity).State = EntityState.Modified;
            await DbContext.SaveChangesAsync(cancellationToken);
            return entity;
        }

        public virtual async Task UpdateManyAsync(IEnumerable<TEntity> entities, CancellationToken cancellationToken = default)
        {
            foreach (var entity in entities)
            {
                DbContext.Entry(entity).State = EntityState.Modified;
            }
            await DbContext.SaveChangesAsync(cancellationToken);
        }

        public virtual async Task DeleteAsync(TKey id, CancellationToken cancellationToken = default)
        {
            var entity = await GetAsync(id, cancellationToken: cancellationToken);
            if (entity != null)
            {
                await DeleteAsync(entity, cancellationToken);
            }
        }

        public virtual async Task DeleteAsync(TEntity entity, CancellationToken cancellationToken = default)
        {
            DbSet.Remove(entity);
            await DbContext.SaveChangesAsync(cancellationToken);
        }

        public virtual async Task DeleteManyAsync(IEnumerable<TKey> ids, CancellationToken cancellationToken = default)
        {
            var entities = await DbSet.Where(x => ids.Contains(x.Id)).ToListAsync(cancellationToken);
            if (entities.Any())
            {
                DbSet.RemoveRange(entities);
                await DbContext.SaveChangesAsync(cancellationToken);
            }
        }

        public virtual async Task DeleteManyAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default)
        {
            var entities = await DbSet.Where(predicate).ToListAsync(cancellationToken);
            if (entities.Any())
            {
                DbSet.RemoveRange(entities);
                await DbContext.SaveChangesAsync(cancellationToken);
            }
        }

        public virtual async Task<bool> ExistsAsync(TKey id, CancellationToken cancellationToken = default)
        {
            return await DbSet.AnyAsync(e => e.Id.Equals(id), cancellationToken);
        }

        public virtual async Task<bool> ExistsAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default)
        {
            return await DbSet.AnyAsync(predicate, cancellationToken);
        }

        protected virtual IQueryable<TEntity> IncludeDetails(IQueryable<TEntity> query)
        {
            // 在子类中重写此方法以包含导航属性
            return query;
        }

        protected virtual IQueryable<TEntity> ApplySorting(IQueryable<TEntity> query, List<SortingRequest> sortingRequests)
        {
            if (!sortingRequests.Any())
            {
                return query;
            }

            var firstSort = sortingRequests.First();
            var orderedQuery = firstSort.IsAscending
                ? query.OrderBy(GetSortExpression(firstSort.SortField))
                : query.OrderByDescending(GetSortExpression(firstSort.SortField));

            foreach (var sort in sortingRequests.Skip(1))
            {
                orderedQuery = sort.IsAscending
                    ? orderedQuery.ThenBy(GetSortExpression(sort.SortField))
                    : orderedQuery.ThenByDescending(GetSortExpression(sort.SortField));
            }

            return orderedQuery;
        }

        protected virtual Expression<Func<TEntity, object>> GetSortExpression(string sortField)
        {
            var parameter = Expression.Parameter(typeof(TEntity), "x");
            var property = Expression.Property(parameter, sortField);
            var conversion = Expression.Convert(property, typeof(object));
            return Expression.Lambda<Func<TEntity, object>>(conversion, parameter);
        }
    }
} 